﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Web;
using System.Net;
using System.Xml;
using System.Xml.Serialization;

namespace LabelPrintSystem
{
    class MicroWebHelper
    {
        const string FLAG = "result";
        XmlDocument _docReceive = null;
        Hashtable _xmlNamespaces = new System.Collections.Hashtable();

        public XmlDocument QueryPostWebService(String URL, String MethodName, System.Collections.Hashtable Pars)
        {
            HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(URL + "/" + MethodName);
            request.Method = "POST";
            request.ContentType = "application/x-www-form-urlencoded";
            SetWebRequest(request);
            byte[] data = EncodePars(Pars);
            WriteRequestData(request, data);
            return ReadXmlResponse(request.GetResponse());
        }

        public XmlDocument QueryGetWebService(String URL, String MethodName, Hashtable Pars)
        {
            HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(URL + "/" + MethodName + "?" + ParsToString(Pars));
            request.Method = "GET";
            request.ContentType = "application/x-www-form-urlencoded";
            SetWebRequest(request);
            return ReadXmlResponse(request.GetResponse());
        }

        public XmlDocument QuerySoapWebService(String URL, String MethodName, Hashtable Pars, string soapVersion = "1.1", bool isSpecial = false)
        {
            if (_xmlNamespaces.ContainsKey(URL))
            {
                return QuerySoapWebService(URL, MethodName, Pars, _xmlNamespaces[URL].ToString(), soapVersion, isSpecial);
            }
            else
            {
                return QuerySoapWebService(URL, MethodName, Pars, GetNamespace(URL), soapVersion, isSpecial);
            }
        }

        private XmlDocument QuerySoapWebService(String URL, String MethodName, Hashtable Pars, string XmlNs, string soapVersion, bool isSpecial)
        {
            _xmlNamespaces[URL] = XmlNs;
            HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(URL);
            request.Method = "POST";

            if (soapVersion == "1.2")
            {
                request.ContentType = "application/soap+xml;charset=utf-8";
            }
            else
            {
                request.ContentType = "text/xml; charset=utf-8";
                request.Headers.Add("SOAPAction", "\"" + XmlNs + (XmlNs.EndsWith("/") ? "" : "/") + MethodName + "\"");
            }
            SetWebRequest(request);
            byte[] data = EncodeParsToSoap(Pars, XmlNs, MethodName, soapVersion, isSpecial);
            WriteRequestData(request, data);

            XmlDocument docResult = new XmlDocument();
            IAsyncResult ar = request.BeginGetResponse(AsyncCallbackGetResponse, request);
            //XmlDocument doc = new XmlDocument();
            //doc = ReadXmlResponse(request.GetResponse());
            if (ar.IsCompleted && _docReceive != null)
            {
                XmlNamespaceManager mgr = new XmlNamespaceManager(_docReceive.NameTable);
                String RetXml = "";
                if (soapVersion == "1.2")
                {
                    mgr.AddNamespace("soap12", "http://www.w3.org/2003/05/soap-envelope");
                    XmlNode node = _docReceive.SelectSingleNode("//soap12:Body/*/*", mgr);
                    if (node == null) return null;
                    RetXml = node.InnerXml;
                }
                else
                {
                    mgr.AddNamespace("soap", "http://schemas.xmlsoap.org/soap/envelope/");
                    XmlNode node = _docReceive.SelectSingleNode("//soap:Body/*/*", mgr);
                    if (node == null) return null;
                    RetXml = node.InnerXml;
                }
                docResult.LoadXml("<" + FLAG + ">" + RetXml + "</" + FLAG + ">");
                AddDelaration(docResult);
            }
            return docResult;
        }

        private void AsyncCallbackGetResponse(IAsyncResult ar)
        {
            WebRequest request = ar.AsyncState as WebRequest;
            var response = request.EndGetResponse(ar);
            Stream stream = response.GetResponseStream();
            using (StreamReader reader = new StreamReader(stream))
            {
                string content = reader.ReadToEnd();
                if (!string.IsNullOrEmpty(content))
                {
                    _docReceive = new XmlDocument();
                    _docReceive.LoadXml(content);
                }
            }
        }

        private string GetNamespace(String URL)
        {
            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(URL + "?WSDL");
            SetWebRequest(request);
            WebResponse response = request.GetResponse();
            XmlDocument doc = null;
            using (StreamReader sr = new StreamReader(response.GetResponseStream(), Encoding.UTF8))
            {
                doc = new XmlDocument();
                doc.LoadXml(sr.ReadToEnd());
                sr.Close();
            }
            return doc != null ? doc.SelectSingleNode("//@targetNamespace").Value : "http://tempuri.org/";
        }

        private byte[] EncodeParsToSoap(Hashtable Pars, String XmlNs, String MethodName, string soapVersion, bool isSpecial)
        {
            XmlDocument doc = new XmlDocument();
            XmlElement soapBody = null;

            if (soapVersion == "1.2")
            {
                doc.LoadXml("<soap12:Envelope xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soap12=\"http://www.w3.org/2003/05/soap-envelope\"></soap12:Envelope>");
                AddDelaration(doc);
                soapBody = doc.CreateElement("soap12", "Body", "http://www.w3.org/2003/05/soap-envelope");
            }
            else
            {
                doc.LoadXml("<soap:Envelope xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\"></soap:Envelope>");
                AddDelaration(doc);
                soapBody = doc.CreateElement("soap", "Body", "http://schemas.xmlsoap.org/soap/envelope/");
            }

            if (isSpecial)
            {
                XmlElement soapHeader = null;
                if (soapVersion == "1.2")
                {
                    soapHeader = doc.CreateElement("soap12", "Header", "http://www.w3.org/2003/05/soap-envelope");
                }
                else
                { soapHeader = doc.CreateElement("soap", "Header", "http://schemas.xmlsoap.org/soap/envelope/"); }
               
                XmlElement soapMethod0 = doc.CreateElement("AuthHeaderGB");
                soapMethod0.SetAttribute("xmlns", XmlNs);
                foreach (string k in Pars.Keys)
                {
                    XmlElement soapPar = doc.CreateElement(k);
                    soapPar.InnerXml = ObjectToSoapXml(Pars[k]);
                    soapMethod0.AppendChild(soapPar);
                }
                soapHeader.AppendChild(soapMethod0);
                doc.DocumentElement.AppendChild(soapHeader);
            }

            XmlElement soapMethod = doc.CreateElement(MethodName);
            soapMethod.SetAttribute("xmlns", XmlNs);
            if (!isSpecial)
                foreach (string k in Pars.Keys)
                {
                    XmlElement soapPar = doc.CreateElement(k);
                    soapPar.InnerXml = ObjectToSoapXml(Pars[k]);
                    soapMethod.AppendChild(soapPar);
                }
            soapBody.AppendChild(soapMethod);
            doc.DocumentElement.AppendChild(soapBody);             

            return Encoding.UTF8.GetBytes(doc.OuterXml);
        }

        private string ObjectToSoapXml(object o)
        {
            XmlSerializer mySerializer = new XmlSerializer(o.GetType());
            MemoryStream ms = new MemoryStream();
            mySerializer.Serialize(ms, o);
            XmlDocument doc = new XmlDocument();
            doc.LoadXml(Encoding.UTF8.GetString(ms.ToArray()));
            if (doc.DocumentElement != null)
            {
                return doc.DocumentElement.InnerXml;
            }
            else
            {
                return o.ToString();
            }
        }

        private void SetWebRequest(HttpWebRequest request)
        {
            request.Credentials = CredentialCache.DefaultCredentials;
            request.Timeout = 10000;
        }

        private void WriteRequestData(HttpWebRequest request, byte[] data)
        {
            request.ContentLength = data.Length;
            using (Stream writer = request.GetRequestStream())
            {
                writer.Write(data, 0, data.Length);
                writer.Close();
            }
        }

        private byte[] EncodePars(Hashtable Pars)
        {
            return Encoding.UTF8.GetBytes(ParsToString(Pars));
        }

        private String ParsToString(Hashtable Pars)
        {
            StringBuilder sb = new StringBuilder();
            foreach (string k in Pars.Keys)
            {
                if (sb.Length > 0)
                {
                    sb.Append("&");
                }
                sb.Append(HttpUtility.UrlEncode(k) + "=" + HttpUtility.UrlEncode(Pars[k].ToString()));
            }
            return sb.ToString();
        }

        private XmlDocument ReadXmlResponse(WebResponse response)
        {
            String retXml = "";
            using (StreamReader sr = new StreamReader(response.GetResponseStream(), Encoding.UTF8))
            {
                retXml = sr.ReadToEnd();
                sr.Close();
            }
            XmlDocument doc = new XmlDocument();
            if (!string.IsNullOrEmpty(retXml)) { doc.LoadXml(retXml); }
            return doc;
        }

        private void AddDelaration(XmlDocument doc)
        {
            XmlDeclaration decl = doc.CreateXmlDeclaration("1.0", "utf-8", null);
            doc.InsertBefore(decl, doc.DocumentElement);
        }
    }
}
